/*
 * Copyright (c) 2012, marco.tamburelli@gmail.com
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.bmi.gwt.mi.server.core.event;

import java.util.List;

import javax.servlet.ServletContext;

import org.bmi.gwt.mi.server.channel.ChannelFactory;
import org.bmi.gwt.mi.server.events.MiEvent;
import org.bmi.gwt.mi.server.events.QueueEvent;
import org.bmi.gwt.mi.server.impl.ParamUtils;
import org.bmi.gwt.mi.shared.communication.call.Call;
import org.bmi.gwt.mi.shared.communication.call.ConstructorCall;
import org.bmi.gwt.mi.shared.communication.call.FactoryCall;
import org.bmi.gwt.mi.shared.communication.call.MethodCall;
import org.bmi.gwt.mi.shared.communication.ptypes.Param;
import org.bmi.gwt.mi.shared.exceptions.InvalidArgumentException;

/**
 * For internal use only.
 */
abstract class AbstractMiEvent implements MiEvent
{
	private final Call call;
	private Class<?>[] argTypes;

	private final QueueEventImpl targetQueueEvent;

	AbstractMiEvent(String queueName, Call call, Class<?>[] argTypes, ChannelFactory channelFactory,
			ServletContext servletContext)
	{
		this.call = call;
		this.argTypes = argTypes;

		this.targetQueueEvent = new QueueEventImpl(channelFactory, queueName, servletContext);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T getMethodArgument(int idx) throws InvalidArgumentException
	{
		List<Param<?>> args;

		Call call = this.call;

		if (call instanceof MethodCall)
		{
			MethodCall _call = (MethodCall) call;
			args = _call.getArguments();
		}
		else if (call instanceof FactoryCall)
		{
			FactoryCall _call = (FactoryCall) call;
			args = _call.getFactoryMethodArgs();
		}
		else if (call instanceof ConstructorCall)
		{
			ConstructorCall _call = (ConstructorCall) call;
			args = _call.getConstructorArgs();
		}
		else
			/* Shouldn't be possible */
			return null;

		if (args.size() != this.argTypes.length)
			throw new InvalidArgumentException(
					"Parameters types defined on handler annotation doesn't match method parameter.");

		if (idx >= args.size())
			throw new InvalidArgumentException("Provided argument index exceeds the size of arguments array.");

		Object v = ParamUtils.getValue(args.get(idx), this.argTypes[idx]);

		return (T) v;
	}

	@Override
	public <T> void setMethodArgument(T value, int idx) throws InvalidArgumentException
	{
		List<Param<?>> args;

		Call call = this.call;

		if (call instanceof MethodCall)
		{
			MethodCall _call = (MethodCall) call;
			args = _call.getArguments();
		}
		else if (call instanceof FactoryCall)
		{
			FactoryCall _call = (FactoryCall) call;
			args = _call.getFactoryMethodArgs();
		}
		else if (call instanceof ConstructorCall)
		{
			ConstructorCall _call = (ConstructorCall) call;
			args = _call.getConstructorArgs();
		}
		else
			/* Shouldn't be possible */
			return;

		if (idx >= args.size())
			throw new InvalidArgumentException("Provided argument index exceeds the size of arguments array.");

		Param<?> param = args.get(idx);

		ParamUtils.setValue(param, value);
	}

	@Override
	public abstract void setEnabled(boolean enabled);

	@Override
	public abstract boolean isEnabled();

	@Override
	public String getObjectId()
	{
		return this.call.getObjectId();
	}

	@Override
	public QueueEvent getTargetQueueEvent()
	{
		return this.targetQueueEvent;
	}
}
